/** @brief Define the transaction log buffer that each transaction has. 
 *
 *  transLogBuffer.h
 *  kangmodb
 *
 *  Created by 강모 김 on 11. 4. 24..
 *  Copyright 2011 강모소프트. All rights reserved.
 *
 */

#ifndef _KD_TRANS_LOG_BUFFER_H_
#define _KD_TRANS_LOG_BUFFER_H_ (1)

#include "types.h"
#include "key.h"
#include "Data.h"


/** @brief Log types stored in type field in stgLogRecordHeader 
 */
const u8 STG_LT_INSERT = 1;
const u8 STG_LT_UPDATE = 2;
const u8 STG_LT_DELETE = 3;
const u8 STG_LT_SAVEPOINT = 4;

/** @brief Each log record begins with a header including log type.
 */
typedef struct stgLogRecordHeader
{
	/** @brief The type of a log.
	 */
	u8 type;
} stgLogRecordHeader;

/** @brief Each log record ends with the same data with header.
 */
typedef struct stgLogRecordTail
{
	/** @brief The size of the log body. This size does not include the size of log header and tail.
	 *  The bodySize field in the log tail can be used for traversing log records in reverse order in a transaction.
     *  Because KangmoDB first writes a transaction's log in its own transaction log buffer, no other transactions' log exists in the buffer.
     *  Upon transaction commit, the log is sent to a durable Log Stream where multiple transactions concurrently send log data, but transactions do not intervene each other in the Log Stream.
	 */
	u16 bodySize;
} stgLogRecordTail;


/** @brief LogFormatter that formats log records for reading and writing.
*/
namespace stgLogFormatter {
	/** @brief Check if the log record is for an insertion.
	 */
	KD_INLINE bool isInsertLog(const void * logRecord)
	{
		KD_DASSERT( logRecord != NULL );

		return ((stgLogRecordHeader*)logRecord)->type == STG_LT_INSERT;
	}

	/** @brief Check if the log record is for an update.
	 */
	KD_INLINE bool isUpdateLog(const void * logRecord)
	{
		KD_DASSERT( logRecord != NULL );

		return ((stgLogRecordHeader*)logRecord)->type == STG_LT_UPDATE;
	}

	/** @brief Check if the log record is for a deletion.
	 */
	KD_INLINE bool isDeleteLog(const void * logRecord)
	{
		KD_DASSERT( logRecord != NULL );

		return ((stgLogRecordHeader*)logRecord)->type == STG_LT_DELETE;
	}

	/** @brief Check if the log record is for a savepoint.
	 */
	KD_INLINE bool isSavepointLog(const void * logRecord)
	{
		KD_DASSERT( logRecord != NULL );

		return ((stgLogRecordHeader*)logRecord)->type == STG_LT_SAVEPOINT;
	}
	
	/** @brief Calculate the size of an insertion log record.
	 */
	KD_INLINE int insertLogBodySize(const stgKey & key, const stgData & data)
	{
		return	sizeof(stgTableId) +	 // The tableId 
		        sizeof(stgValueLength) + // The length of key
				key.getLength() +		 // The key itself
				sizeof(stgValueLength) + // The length of data
				data.getLength();		 // The data itself
	}

	/** @brief Calculate the size of an update log record.
	 */
	KD_INLINE int updateLogBodySize(const stgKey & key, const stgData & data)
	{
		return	sizeof(stgTableId) +	 // The tableId 
		        sizeof(stgValueLength) + // The length of key
				key.getLength() +		 // The key itself
				sizeof(stgValueLength) + // The length of data
				data.getLength();		 // The data itself
	}

	/** @brief Calculate the size of a deletion log record.
	 */
	KD_INLINE int deleteLogBodySize(const stgKey & key)
	{
		return	sizeof(stgTableId) +	 // The tableId 
		        sizeof(stgValueLength) + // The length of key
				key.getLength(); 		 // The key itself
	}

	/** @brief Calculate the size of a savepoint log record.
	 */
	KD_INLINE int savepointLogSize()
	{
		return	sizeof(stgTableId) +     // The tableId 
		        sizeof(stgSavepointId) ; // The savepointId
	}

	/** @brief write an insert log record whose size is logRecordSize to the destination. 
	 */
	KD_INLINE void writeInsertLog(void * dest, int logRecordSize, stgTableId tableId, const stgKey & key, const stgData & data)
	{
		// TODO : Throw an error code instead if hitting assertion.
		KD_ASSERT( logRecordSize <= KD_MAX_U16 );
		
		KD_DASSERT( dest != NULL );
		KD_DASSERT( logRecordSize > 0 );
		KD_DASSERT( tableId >= 0 );

		// TODO : Implement
		KD_ASSERT(0);
	}

	/** @brief write an update log record whose size is logRecordSize to the destination. 
	 */
	KD_INLINE void writeUpdateLog(void * dest, int logRecordSize, stgTableId tableId, const stgKey & key, const stgData & data)
	{
		// TODO : Throw an error code instead if hitting assertion.
		KD_ASSERT( logRecordSize <= KD_MAX_U16 );
		
		KD_DASSERT( dest != NULL );
		KD_DASSERT( logRecordSize > 0 );
		KD_DASSERT( tableId >= 0 );

		// TODO : Implement
		KD_ASSERT(0);
	}

	/** @brief write a delete log record whose size is logRecordSize to the destination. 
	 */
	KD_INLINE void writeDeleteLog(void * dest, int logRecordSize, stgTableId tableId, const stgKey & key)
	{
		// TODO : Throw an error code instead if hitting assertion.
		KD_ASSERT( logRecordSize <= KD_MAX_U16 );
		
		KD_DASSERT( dest != NULL );
		KD_DASSERT( logRecordSize > 0 );
		KD_DASSERT( tableId >= 0 );

		// TODO : Implement
		KD_ASSERT(0);
	}

	/** @brief write a savepoint log record whose size is logRecordSize to the destination. 
	 */
	KD_INLINE void writeSavepointLog(void * dest, int logRecordSize, stgTableId tableId, stgSavepointId savepointId)
	{
		KD_DASSERT( logRecordSize <= KD_MAX_U16 );
		
		KD_DASSERT( dest != NULL );
		KD_DASSERT( logRecordSize > 0 );
		KD_DASSERT( tableId >= 0 );

		// TODO : Implement
		KD_ASSERT(0);
	}

	/** @brief read an insert log record from a memory location. 
	 */
	KD_INLINE void readInsertLog(const void * src, stgTableId *tableId, stgKey * key, stgData * data)
	{
		KD_DASSERT( src != NULL );
		KD_DASSERT( tableId != NULL );
		KD_DASSERT( key != NULL );
		KD_DASSERT( data != NULL );

		// TODO : Implement
		KD_ASSERT(0);
	}

	/** @brief read an update log record from a memory location. 
	 */
	KD_INLINE void readUpdateLog(const void * src, stgTableId *tableId, stgKey * key, stgData * data)
	{
		KD_DASSERT( src != NULL );
		KD_DASSERT( tableId != NULL );
		KD_DASSERT( key != NULL );
		KD_DASSERT( data != NULL );

		// TODO : Implement
		KD_ASSERT(0);
	}

	/** @brief read a delete log record from a memory location. 
	 */
	KD_INLINE void readDeleteLog(const void * src, stgTableId *tableId, stgKey * key)
	{
		KD_DASSERT( src != NULL );
		KD_DASSERT( tableId != NULL );
		KD_DASSERT( key != NULL );

		// TODO : Implement
		KD_ASSERT(0);
	}

	/** @brief read a savepoint log record from a memory location. 
	 */
	KD_INLINE void readSavepointLog(const void * src, stgTableId * tableId, stgSavepointId * savepointId)
	{
		KD_DASSERT( src != NULL );
		KD_DASSERT( tableId != NULL );
		KD_DASSERT( savepointId != NULL );

		// TODO : Implement
		KD_ASSERT(0);
	}
};


/** @brief The log buffer that each transaction has. All history of modifications of the transaction are kept in this object.
 */
class stgTransactionLogBuffer
{
private :
	// TODO : implement the logbuffer in a continuous memory using memalign.
	// TODO : need research if memalign function is available on iOS and Android devices.
public :

	stgTransactionLogBuffer() {}
	
	/** @brief initialize the log buffer. The memory size of each log chunk will be logChunkSize.
	 */
	KD_VOID initialize(int logChunkSize) 
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Truncate the transaction log buffer up to the cutting address.
	 *  Rollback to Savepoint : 
	 *      All log records after the savepoint are undone in reverse order. 
	 *      Log records after the savepoint (including the savepoint itself) is not necessary any more.
	 *      So the log buffer is truncated at the begining address of the savepoint. 
	 *      IOW, cutAddr points to the starting address of Savepoint log.
 	 *  Total Rollback or Commit : 
	 *      All log records are undone(Total Rollback case) or sent to LogStream(Commit case).
	 *      So no log records are necessary. cutAddr has NULL, meaning to truncate all log records in the buffer. 
	 */
	KD_VOID truncate(const void * cutAddr) 
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Write a Savepoint log with the unique savepoint identifier monotonously increasing from 1.
	 */
	KD_VOID writeSavepointLog(stgTableId tableId, stgSavepointId savepointId)
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
			
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Write a log for an insertion of a (key, data) pair.
	 */
	KD_VOID writeInsertLog(stgTableId tableId, const stgKey & key, const stgData & data)
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Write a log for an update of a (key, data) pair.
	 */
	KD_VOID writeUpdateLog(stgTableId tableId, const stgKey & key, const stgData & data)
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Write a log for an deletion of a (key, data) pair.
	 */
	KD_VOID writeDeleteLog(stgTableId tableId, const stgKey & key)
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Return if the log buffer is empty.
	 */
	bool empty() const
	{
		KD_ASSERT(0);
		return false;
	}

	/** @brief Initialize an interation of multiple log chunks.
	 */
	KD_VOID initLogChunkIteration() const
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Return a log chunk for each time this function is called after initLogChunkIteration() function. If no log chunk exists, *logData is set to NULL, *logDataLength is set to 0
	 */
	KD_VOID iterateLogChunk(void ** logData, int * logDataLength) const
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
	
	/** @brief Initialize a backward interation of multiple log records within all chunks.
	 *  Used for Rollback that does undo log records in reverse order.
	 */
	KD_VOID initLogRecordReverseIteration() const
	{
		KD_TRY
		{
			// TODO : Implement
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}

	/** @brief Return a log record for each time this function is called after initLogRecordBackwardIteration() function. If no log record exists, *logRecord is set to NULL, *logRecordLength is set to 0
	 *  Used for Rollback that does undo log records in reverse order.
	 */
	KD_VOID reverseIterateLogRecord(const void ** logRecord, int * logRecordLength) const
	{
		KD_TRY
		{
			// TODO : Implement
			*logRecord = NULL;
			KD_ASSERT(0);
		}
		KD_CATCH
		KD_FINALLY
		KD_END
	}
};

#endif /* _KD_TRANS_LOG_BUFFER_H_ */